package indi.qing.ws;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import indi.qing.entity.Relation;
import indi.qing.entity.User;
import indi.qing.entity.UserMessage;
import indi.qing.service.RelationService;
import indi.qing.service.UserMessageService;
import indi.qing.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author 熊卿智
 * @since 2022-01-17
 */
@ServerEndpoint(value = "/chat/{userId}")
@Component
@Slf4j
public class ChatEndpoint {

    /**
     * 设置一次性存储数据的list的长度为固定值，每当list的长度达到固定值时，向数据库存储一次
     */
    private static final Integer LIST_SIZE = 3;

    /**
     * 设置在线人数为静态变量
     */
    public static int onlineNumber = 0;


    private static UserMessageService userMessageService;

    private static RelationService relationService;

    private static UserService userService;

    /**
     * 新建list集合存储数据
     */
    private static ArrayList<UserMessage> MessageList = new ArrayList<>();

    /**
     * map(userId,websocket)作为对象添加到集合中
     */
    private static Map<String, ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();

    /**
     * session会话
     */
    private Session session;
    /**
     * 用户id
     */
    private String userId;
    /**
     * 用户名
     */
    private String userName;

    @Autowired
    public void setUserMessageService(UserMessageService userMessageService) {
        ChatEndpoint.userMessageService = userMessageService;
    }
    @Autowired
    public void setRelationService(RelationService relationService) {
        ChatEndpoint.relationService = relationService;
    }
    @Autowired
    public void setUserService(UserService userService) {
        ChatEndpoint.userService = userService;
    }


    /**
     * 监听连接（有用户连接，立马到来执行这个方法）
     * session 发生变化
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("userId") String userId, Session session) {
        log.info("open");
        //每打开一个新的窗口，在线人数onlineNumber++
        onlineNumber++;
        //把新用户id赋给变量
        this.userId = userId;
        //把新用户名赋给变量
        User user = userService.getById(userId);
        this.userName = user.getUsername();
        //把新用户的 session 信息赋给变量
        this.session = session;
        //输出 websocket 信息
        log.info("现在来连接的客户id：" + session.getId() + " userId：" + userId);
        log.info("有新连接加入！ 当前在线人数" + onlineNumber);
        //把自己的信息加入到map当中去，this=当前类（把当前类作为对象保存起来）
        onlineUsers.put(userId, this);
        //获得所有的用户lists
        Set<String> lists = onlineUsers.keySet();
        //好友
        List<Map<String,Object>> friendList = new ArrayList<>();
        // 给好友发送上线信息（新用户上线信息）

        //  获取所有好友列表
        List<Relation> friend = relationService.getFriend(Long.valueOf(userId));
        if (CollectionUtils.isEmpty(friend)) {
            return;
        }
        AtomicInteger onlineFriendNumber = new AtomicInteger();
        friend.stream().forEach(relation -> {
            String user2Id = String.valueOf(relation.getUser2Id());
            //把该好友加入到我的好友中
            Map<String,Object> friendMap = new HashMap<>();
            friendMap.put("userId",user2Id);
            friendMap.put("name",relation.getName());
            // 判断好友是否在线
            if (lists.contains(user2Id)) {
                //设置好友在线
                friendMap.put("state",1);
                onlineFriendNumber.getAndIncrement();
                Relation one = relationService.getOne(new QueryWrapper<Relation>()
                        .lambda()
                        .eq(Relation::getUser1Id, user2Id)
                        .eq(Relation::getUser2Id, userId)
                        .eq(Relation::getState, 1)
                );
                //在线就发我上线了
                if (!ObjectUtils.isEmpty(one)) {
                    //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
                    Map<String, Object> map1 = new HashMap(100);
                    //  返回上线状态
                    map1.put("messageType", 1);
                    //  返回用户名
                    map1.put("userId", userId);
                    //  返回备注名
                    map1.put("name", one.getName());
                    try {
                        //给好友发送上线信息（新用户上线信息）
                        sendMessageTo(JSON.toJSONString(map1), user2Id);
                    } catch (IOException e) {
                        log.info(userName + "上线的时候通知所有人发生了错误");
                    }
                }
            }
            else {
                //设置好友不在线
                friendMap.put("state",0);
            }
            friendList.add(friendMap);
        });
        // 给自己发一条消息：告诉自己现在都有谁在线
        Map<String, Object> map2 = new HashMap(100);
        //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
        map2.put("messageType", 3);
        //把所有用户放入map2
        map2.put("myFriend", friendList);
        //返回好友在线人数
        map2.put("onlineFriendNumber", onlineFriendNumber);
        //返回好友人数
        map2.put("totalFriendNumber", friendList.size());
        // 消息发送指定人（所有的在线用户信息）
        try {
            sendMessageTo(JSON.toJSONString(map2), userId);
        } catch (IOException e) {
            log.info(userName + "上线的时候通知所有人发生了错误");
        }
    }

    /**
     * 监听连接断开（有用户退出，会立马到来执行这个方法）
     */
    @OnClose
    public void onClose() {
        log.info("open");
        //每关闭一个新的窗口，在线人数onlineNumber--
        onlineNumber--;
        //从所有在线用户的map中去除下线用户
        onlineUsers.remove(userId);
        //获得所有的用户lists
        Set<String> lists = onlineUsers.keySet();
        //  获取所有好友列表
        List<Relation> friend = relationService.getFriend(Long.valueOf(userId));
        if (CollectionUtils.isEmpty(friend)) {
            return;
        }
        friend.stream().forEach(relation -> {
            String user2Id = String.valueOf(relation.getUser2Id());
            // 判断好友是否在线
            if (lists.contains(user2Id)) {
                Relation one = relationService.getOne(new QueryWrapper<Relation>()
                        .lambda()
                        .eq(Relation::getUser1Id, user2Id)
                        .eq(Relation::getUser2Id, userId)
                        .eq(Relation::getState, 1)
                );
                //在线就发我下线了
                if (!ObjectUtils.isEmpty(one)) {
                    //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
                    Map<String, Object> map1 = new HashMap(100);
                    //  返回下线状态
                    map1.put("messageType", 2);
                    //  下线用户的用户id
                    map1.put("userId", userId);
                    //  下线用户的备注名
                    map1.put("name", one.getName());
                    try {
                        //发送信息，通知谁下线了
                        sendMessageTo(JSON.toJSONString(map1), user2Id);
                    } catch (IOException e) {
                        log.info(userName + "下线的时候通知所有人发生了错误");
                    }
                }
            }
        });

        //关闭连接前，判断list集合是否有数据，如果有，批量保存到数据库
        if (MessageList.size() < LIST_SIZE) {
            userMessageService.saveBatch(MessageList);
        }
        log.info("有连接关闭！ 当前在线人数" + onlineNumber);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.info("服务端发生了错误" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 监听消息（收到客户端的消息立即执行）
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            log.info("来自客户端消息：" + message + "客户端的id是：" + session.getId());
            //用户发送的信息
            com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(message);
            Integer type = Integer.valueOf(jsonObject.getString("type"));
            //发送的内容
            String textMessage = jsonObject.getString("message");
            //发送人
            String fromUserId = jsonObject.getString("userId");
            //接收人  toUserId == 接收用户id
            String toUserId = jsonObject.getString("toUserId");

            //新建message对象
            UserMessage message1 = new UserMessage();

            //设置发送者的userId
            message1.setUserId(Long.valueOf(fromUserId));
            //设置发送的信息的类型 0-文字 1-图片 2-语音
            message1.setType(type);
            //设置发送的信息
            message1.setMessage(textMessage);
            //设置接收者id
            message1.setToUserId(Long.valueOf(toUserId));

            //批量保存信息
            //将每条记录添加到list集合中
            MessageList.add(message1);
            //判断list集合长度
            if (MessageList.size() == LIST_SIZE) {
                log.info("MessageList="+MessageList);
                userMessageService.saveBatch(MessageList);
                //清空集合
                MessageList.clear();
            }

            //发送消息  -- messageType 1代表上线 2代表下线 3代表在线名单  4代表消息
            Map<String, Object> map1 = new HashMap(100);
            map1.put("messageType", 4);
            map1.put("type",type);
            map1.put("textMessage", textMessage);
            map1.put("fromUserId", fromUserId.toString());

            //消息发送指定人（同步）
            map1.put("toUserId", toUserId.toString());
            sendMessageTo(JSON.toJSONString(map1), toUserId);

        } catch (Exception e) {
            log.info("发生了错误了");
        }
    }

    /**
     * 消息发送指定人
     */
    public void sendMessageTo(String message, String toUserId) throws IOException {
        //遍历所有用户
        for (ChatEndpoint item : onlineUsers.values()) {
            if (item.userId.equals(toUserId)) {
                //消息发送指定人（同步）
                item.session.getBasicRemote().sendText(message);
                break;
            }
        }
    }

}

